/*-------------------------------------------------------------------------
 - Copyright (c) 2024-2025 [XD-AMCC TEAM]
 - [XD-AMCC] is licensed under Mulan PSL v2.
 - You can use this software according to the terms and conditions of the Mulan PSL v2.
 - You may obtain a copy of Mulan PSL v2 at:
 -          http://license.coscl.org.cn/MulanPSL2
 - THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES
 - OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 - TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 - See the Mulan PSL v2 for more details.
 ------------------------------------------------------------------------*/


/**
 * @file
 * @brief Interface of the generic class hash_map.
 *
 * @author  WXQ#XDU
 * @date    2024.11
 */

#pragma once

#ifndef AMCC_HASHMAP_H
#define AMCC_HASHMAP_H

#include "amcc_list.h"

AMCC_DECL_BEGIN


/// Type of hash code of the map key.
/// This is the output of func_keyhash()
typedef unsigned int t_hashcode;

typedef void * t_map_key;   //!< type of the key   part of map element
typedef void * t_map_value; //!< type of the value part of map element

/// Type of hash function on map key.
typedef t_hashcode (* t_mapfunc_keyhash)(t_map_key);

/// Type of compare function for map keys.
///
/// It returns a negative value if K1 < K2,
///    returns zero if K1 == K2,
///    returns a positive value if K1 > K2.
typedef int (* t_mapfunc_keycomp)(t_map_key K1, t_map_key K2);

/// Type of function that free the key and value of map element
typedef void (* t_mapfunc_free)(t_map_key, t_map_value);

/**
 * @brief Calculate the hash code of a string.
 * @param str  pointer to a string
 * @return return the hash code of a string.
 */
extern t_hashcode amcc_hashcode(const char * str);


/**
 * Hash map is the underlying type for symbol table, key-words set.
 * Hash map is also named unordered map.
 */
typedef struct hash_map {
    unsigned int    capacity; //!< Capacity of buckets
    int    count_of_keys;  //!< Counter of elements in map

    // The real type is t_list<t_hashmap_entry_ptr>[capacity]
    t_list_ptr  buckets;

    t_mapfunc_keyhash   fpKeyHash;    //!< function to calculate the hash code of a key
    t_mapfunc_keycomp   fpKeyCompare; //!< function to compare two keys
    t_mapfunc_free      fpFree;  //!< function to free the key and value of each entry
} t_hashmap, * t_hashmap_ptr;


/**
 * @brief Creates a new hash map.
 *
 * @param capacity_init minimal number of buckets to use on initialization
 *                      If it is zero, implementation-defined default value is used
 * @param fpKeyHash     pointer to a hash function
 * @param fpKeyCompare  pointer to a function which compare two keys
 * @param fpFree        pointer to a function which destroys key and value
 *
 * @return On success, return a pointer to the new created map.
 * @return On failure, just as memory allocation failure, return null.
 */
extern t_hashmap_ptr  hashmap_new(unsigned capacity_init,
                                  t_mapfunc_keyhash fpKeyHash,
                                  t_mapfunc_keycomp fpKeyCompare,
                                  t_mapfunc_free    fpFree );


/**
 * @brief  Clean all entries in map.
 * @param pThis  pointer to this map
 */
extern void hashmap_clean(t_hashmap_ptr pThis);

/**
 * @brief Clean all entries and destroy the map.
 * @param pThis  pointer to this map
 */
extern void hashmap_destroy(t_hashmap_ptr pThis);

/**
 * @brief Gets the number of elements in the container.
 * @param pThis pointer to this map
 * @return The number of elements in the container.
 */
extern int  hashmap_size(t_hashmap_ptr pThis);

/**
 * @brief Type of callback functions for traverse a map.
 *
 * @param key    the key part of map entry
 * @param value  the value part of map entry
 * @param user_data pointer to any user data for pass some information
 *    to/from this user function. Its usage and memory are owned by user.
 *
 * @return Return 0 to continue visiting the next entry in map.
 * @return Return a non-zero value to break the traversal.
 *
 * @see hashmap_foreach
 */
typedef int (*t_mapfunc_visit)(t_map_key key, t_map_value value, void* user_data);

/**
 * @brief  Traverse the map.
 *
 * @param pThis       pointer to this map
 * @param fp_visit    pointer to a visit function on map entry
 * @param user_data   pointer to any user data for pass some information
 *    to/from this user function. Its usage and memory are owned by user.
 *    This parameter will be pass to fp_visit.
 *
 * @return Return 0 if each entry in map has been visited.
 * @return Return the non-zero value from user's action
 *   if whole traversal are broken on one entry.
 */
extern int hashmap_foreach(t_hashmap_ptr pThis, t_mapfunc_visit fp_visit, void* user_data);

/**
 * @brief  Find the mapped value of the specified key.
 * @param pThis   pointer to this map
 * @param key     a map key
 * @return Return the value if the key is found, otherwise return null.
 */
extern t_map_value hashmap_find(t_hashmap_ptr pThis, t_map_key key);

/// Result for hashmap_put() and hashmap_remove().
typedef struct mapresult_insert {
    t_map_value  value;
    bool         success;
} t_mapresult_insert;

/**
 * @brief  Put an element into map.
 *
 * If the key is already exists in map, this map keeps unchanged.
 * <p>
 * This table explains four cases of the returned "t_mapresult_insert":
 *
 * <TABLE border=1>
 * <TR>
 *   <TH> value </TH><TH> success </TH><TH>  note </TH>
 * </TR>
 * <TR>
 *   <TD> NULL &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp;  </TD>
 *   <TD> true &nbsp; &nbsp;&nbsp; &nbsp;  </TD>
 *   <TD> a new map entry is created </TD>
 * </TR> <TR>
 *   <TD>  NULL  </TD><TD>  false  </TD>
 *   <TD> fail on memory allocation or invalid parameter(s) </TD>
 * </TR> <TR>
 *   <TD>  non-NULL </TD><TD> true  </TD>
 *   <TD> NEVER appear </TD>
 * </TR> <TR>
 *   <TD> non-NULL </TD><TD> false  </TD>
 *   <TD> the key already exists, and return the value in map </TD>
 * </TR>
 * </TABLE>
 *
 * @param pThis  pointer to this map
 * @param key    the key of new element
 * @param value  the value of new element
 * @return Return a pair object whose field 'success' indicating whether
 *   the element was successfully inserted or not, and
 *   another field 'value' is the last mapped value whose key is
 *   equivalent to the parameter 'key'.
 */
extern t_mapresult_insert  hashmap_put(t_hashmap_ptr pThis,
                                       t_map_key key, t_map_value value);

/**
 * @brief  Remove an element from map.
 *
 * If the key is already exists in map, the entry is destroyed.
 * <p>
 * This table explains four cases of the returned "t_mapresult_insert":
 *
 * <TABLE border=1>
 * <TR>
 *   <TH> value </TH><TH> success </TH><TH>  note </TH>
 * </TR>
 * <TR>
 *   <TD> NULL &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp;  </TD>
 *   <TD> true &nbsp; &nbsp;&nbsp; &nbsp;  </TD>
 *   <TD> NEVER appear </TD>
 * </TR> <TR>
 *   <TD>  NULL  </TD>
 *   <TD>  false  </TD>
 *   <TD>  key does not exit or invalid parameter(s) </TD>
 * </TR> <TR>
 *   <TD>  non-NULL </TD><TD> true  </TD>
 *   <TD> a map entry is removed </TD>
 * </TR> <TR>
 *   <TD> non-NULL </TD><TD> false  </TD>
 *   <TD> NEVER appear </TD>
 * </TR>
 * </TABLE>
 *
 * @param pThis  pointer to thus map
 * @param key    the key of new element
 *
 * @return Return a pair object whose field 'success' indicating whether
 *   the element was removed or not, and
 *   another field 'value' is the last mapped value whose key is
 *   equivalent to the parameter 'key'.
 *
 * @note The returned t_mapresult_insert.value may be destroyed
 *       by map.fpFree().
 */
extern t_mapresult_insert  hashmap_remove(t_hashmap_ptr pThis, t_map_key key);


AMCC_DECL_END

#endif // #ifndef AMCC_HASHMAP_H
